/*
 * Copyright 2007 Michał Baliński
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package pl.balon.gwt.diagrams.client.connection;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import pl.balon.gwt.diagrams.client.connection.calculator.ConnectionDataCalculator;
import pl.balon.gwt.diagrams.client.connection.data.ConnectionData;
import pl.balon.gwt.diagrams.client.connector.Connector;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.dom.client.HasMouseOverHandlers;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * Base widget class for connections.
 *
 * @author Michał Baliński (michal.balinski@gmail.com)
 */
public abstract class AbstractConnection extends Widget implements Connection, HasMouseOverHandlers, HasClickHandlers{
	public String id=null;

	public String getId() {
		if(id==null) {
			createId();
		}
		return id;
	}

	public void createId() {
		//Later use a uuid generator
		this.setId(Math.random()/(Math.random()*1000)+Long.toString(System.currentTimeMillis())+Math.random()*(Math.random()*1000)
				);
	}

	public void setId(String id) {
		if(id==null) {
			this.id=id;
		}
	}

	public abstract String getType();
	/**
	 * List of connectors involved in this connection.
	 */
	protected final List<Connector> connected = new ArrayList<Connector>();

	/**
	 * Component responsible for connection computation.
	 */
	private ConnectionDataCalculator calculator;


	/**
	 * The actual points.
	 */
	protected ConnectionData data;

	public ConnectionData getConnectionData() {
		return this.data;
	}


	/**
	 * Constructs connection which connects list of connectors.
	 *
	 * @param toConnect Connectors to connect
	 */
	public AbstractConnection(List<Connector> toConnect) {
		connected.addAll(toConnect);
		setCalculator(createCalculator());
		for (Iterator<Connector> i = connected.iterator(); i.hasNext();) {
			Connector c = i.next();
			c.connect(this);
		}
	}

	public List<Connector> getConnectors(){
		return connected;
	}
	/**
	 * Constructs connection which connects list of connectors.
	 *
	 * @param toConnect Connectors to connect
	 */
	public AbstractConnection(Connector toConnect[]) {
		this(Arrays.asList(toConnect));
	}

	/**
	 * @see pl.balon.gwt.diagrams.client.connection.Connection#remove()
	 */
	public void remove(){
		for (ListIterator<Connector> i = connected.listIterator(); i.hasNext();) {
			Connector c = i.next();
			i.remove();
			c.disconnect(this);
		}
		removeFromParent();
	}


	/**
	 * @see pl.balon.gwt.diagrams.client.connection.Connection#disconnect(pl.balon.gwt.diagrams.client.connector.Connector)
	 */
	public void disconnect(Connector c){
		int index = connected.indexOf(c);
		if( index > -1 ){
			connected.remove(c);
			c.disconnect(this);
		}
	}

	/**
	 * @see pl.balon.gwt.diagrams.client.connection.Connection#update()
	 */
	public void update(){
		if( isAttached() ) {
			this.data = computeConnectionData();
			update(data);
		}
	}

	@Override
	protected void onDetach() {
		super.onDetach();
		// TODO Implement sth. ?
	}

	@Override
	protected void onAttach() {
		super.onAttach();
		update();
	}

	/**
	 * Delegates connection computation to ConnectionDataCalculator
	 *
	 * @return connection data
	 */
	private ConnectionData computeConnectionData() {
		return calculator.createConnectionData(connected);
	}

	/**
	 * Redraws connection base on connection data
	 *
	 * @param data connection data
	 */
	public abstract void update(ConnectionData data);

	/**
	 * @see pl.balon.gwt.diagrams.client.connection.Connection#appendTo(com.google.gwt.user.client.ui.AbsolutePanel)
	 */
	public void appendTo(AbsolutePanel panel) {
		panel.add(this);
		update();
	}

	/**
	 * @return new connection data calculator
	 */
	protected abstract ConnectionDataCalculator createCalculator();

	/**
	 * @return calculator
	 */
	public ConnectionDataCalculator getCalculator() {
		return calculator;
	}

	/**
	 * @param calculator
	 */
	public void setCalculator(ConnectionDataCalculator calculator) {
		this.calculator = calculator;
	}


	public HandlerRegistration addClickHandler(ClickHandler handler) {
		return addDomHandler(handler, ClickEvent.getType());
	}

	public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
		return addDomHandler(handler, MouseOverEvent.getType());
	}

	public HandlerRegistration addChangeHandler(ChangeHandler handler) {
		return addDomHandler(handler, ChangeEvent.getType());
	}

}
